#include "IL\ILRender.h"

#include "../DWMRICore/Fiber.h"

#include <GL\glui.h>
#include <GL\glut.h>


CFiber **fibers = NULL;
int *count = NULL;
int w, h;

int fiber_length = 30;

float max_x = -FLT_MAX;
float max_y = -FLT_MAX;
float max_z = -FLT_MAX;
float min_x = FLT_MAX;
float min_y = FLT_MAX;
float min_z = FLT_MAX;


/* glui */
int main_window;
float rotation[16] = {
	1, 0, 0, 0, 
	0, 1, 0, 0, 
	0, 0, 1, 0, 
	0, 0, 0, 1
};
float translate_z = 0.0f;
float translate_xy[] = {0.0f, 0.0f};

/* IL */
ILines::ILRender render;
int *firstID = NULL;
int *vertexCount = NULL;
float *vertices = NULL;
float *colors = NULL;
int fiberCount = 0;

void CreateILRender()
{
	/* create IL render */
	render.setErrorCallback(NULL);
	//m_ILID = ILines::ILRender::IL_INVALID_IDENTIFIER;
	float ka = 0.6f, kd = 0.6f, ks = 2.5f, gloss = 10.0f;
	int	texDim = 512;
	GLfloat		lightDirection[4] = {0.0f, 0.0f, 1.0f};
	GLfloat		lightPosition[4] =  {0.0f, 0.0f, 0.0f, 1.0f};
	if (ILines::ILRender::isLightingModelSupported(ILines::ILLightingModel::IL_CYLINDER_PHONG)) {
		render.setupTextures(ka, kd, ks, gloss, texDim, ILines::ILLightingModel::IL_CYLINDER_PHONG, 
			false, lightDirection);
	} 
}

void myGlutIdle( void )
{
  /* According to the GLUT specification, the current window is 
     undefined during an idle callback.  So we need to explicitly change
     it if necessary */
  if ( glutGetWindow() != main_window ) 
    glutSetWindow(main_window);  

  glutPostRedisplay();
}


void myGlutReshape( int x, int y )
{
	float xy_aspect;

	xy_aspect = (float)x / (float)y;
	glViewport( 0, 0, x, y );

	glMatrixMode( GL_PROJECTION );
	glLoadIdentity();
	glFrustum( -xy_aspect*.08, xy_aspect*.08, -.08, .08, .1, 100.0 );

	glutPostRedisplay();
}

void myGlutDisplay( void )
{
	glClearDepth(1.0f);
	glClearColor( .0f, .0f, .0f, 1.0f );
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();

	glMatrixMode(GL_PROJECTION);
	
	glPushMatrix();

	glTranslatef(0.0, 0.0, -1.0);
	glTranslatef(translate_xy[0]/100.0f, translate_xy[1]/100.0f, translate_z/100.0f);
	glMultMatrixf(rotation);

	// render fibers
	//glEnableClientState(GL_VERTEX_ARRAY);
	//glEnableClientState(GL_COLOR_ARRAY);

	//glVertexPointer(3, GL_FLOAT, 0, vertices);
	//glColorPointer(4, GL_FLOAT, 0, colors);

	glDisable(GL_DEPTH_TEST);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_LINE_SMOOTH);
	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
	glLineWidth(1.0f);
	//glDepthMask(GL_FALSE);

	float dx = max_x - min_x;
	float dy = max_y - min_y;
	float dz = max_z - min_z;
	float mx = (max_x + min_x) / 2.0f;
	float my = (max_y + min_y) / 2.0f;
	float mz = (max_z + min_z) / 2.0f;
	float scale = 0.6f / dy;

	for (int y = 0; y < h; y++) {
		for (int x = 0; x < w; x++) {
			int i = y * w + x;

			for (int j = 0; j < count[i]; ++j) {
				//if ((fibers[i][j].m_fCount + fibers[i][j].m_bCount) < 4)
				//	continue;

				int fCount = fibers[i][j].m_fCount > fiber_length ? fiber_length : fibers[i][j].m_fCount;
				int bCount = fibers[i][j].m_bCount > fiber_length ? fiber_length : fibers[i][j].m_bCount;

				glBegin(GL_LINES);
				for (int p = 1; p < fCount; ++p) {
					float x = (fibers[i][j].m_pF[p].m_x - mx) * scale;
					float y = (fibers[i][j].m_pF[p].m_y - my) * scale;
					float z = (fibers[i][j].m_pF[p].m_z - mz) * scale;
					glColor4f(fibers[i][j].m_pFColor[p].m_x, fibers[i][j].m_pFColor[p].m_y, fibers[i][j].m_pFColor[p].m_z, 0.9f);
					glVertex3f(x, y, z);


					x = (fibers[i][j].m_pF[p-1].m_x - mx) * scale;
					y = (fibers[i][j].m_pF[p-1].m_y - my) * scale;
					z = (fibers[i][j].m_pF[p-1].m_z - mz) * scale;
					glColor4f(fibers[i][j].m_pFColor[p-1].m_x, fibers[i][j].m_pFColor[p-1].m_y, fibers[i][j].m_pFColor[p-1].m_z, 0.9f);
					glVertex3f(x, y, z);

				}
				for (int p = 1; p < bCount; ++p) {
					float x = (fibers[i][j].m_pB[p].m_x - mx) * scale;
					float y = (fibers[i][j].m_pB[p].m_y - my) * scale;
					float z = (fibers[i][j].m_pB[p].m_z - mz) * scale;
					glColor4f(fibers[i][j].m_pBColor[p].m_x, fibers[i][j].m_pBColor[p].m_y, fibers[i][j].m_pBColor[p].m_z, 0.9f);
					glVertex3f(x, y, z);

					x = (fibers[i][j].m_pB[p-1].m_x - mx) * scale;
					y = (fibers[i][j].m_pB[p-1].m_y - my) * scale;
					z = (fibers[i][j].m_pB[p-1].m_z - mz) * scale;
					glColor4f(fibers[i][j].m_pBColor[p-1].m_x, fibers[i][j].m_pBColor[p-1].m_y, fibers[i][j].m_pBColor[p-1].m_z, 0.9f);
					glVertex3f(x, y, z);

				}
				glEnd();
			}
		}
	}

	render.multiDrawArrays(firstID, vertexCount, fiberCount);

	glDisable(GL_LINE_SMOOTH);
	glDisable(GL_BLEND);
	glEnable(GL_DEPTH_TEST);
	//glDepthMask(GL_TRUE);

	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_COLOR_ARRAY);
	// end

	//glutWireSphere(0.5f, 32, 32);

	glutSwapBuffers();

	glPopMatrix();
}

void CreateVertexAndColorBuffer(const char *confPathname, const char *vertPathname)
{
	/* read fibers */
	ReadFibers(confPathname, vertPathname, &fibers, &count, w, h);

	fiberCount = 0;
	int totalVericesCount = 0;

	for (int i = 0; i < w*h; ++i) {
		for (int j = 0; j < count[i]; ++j) {
			if (fibers[i][j].m_fCount > 1 && fibers[i][j].m_bCount > 1) {
				fiberCount++;
				totalVericesCount += (fibers[i][j].m_fCount + fibers[i][j].m_bCount - 1);
				fibers[i][j].ComputeColor();


				for (int p = fibers[i][j].m_bCount-1; p > 0; --p) {
					if (fibers[i][j].m_pB[p].m_x > max_x)
						max_x = fibers[i][j].m_pB[p].m_x;
					if (fibers[i][j].m_pB[p].m_y > max_y)
						max_y = fibers[i][j].m_pB[p].m_y;
					if (fibers[i][j].m_pB[p].m_z > max_z)
						max_z = fibers[i][j].m_pB[p].m_z;

					if (fibers[i][j].m_pB[p].m_x < min_x)
						min_x = fibers[i][j].m_pB[p].m_x;
					if (fibers[i][j].m_pB[p].m_y < min_y)
						min_y = fibers[i][j].m_pB[p].m_y;
					if (fibers[i][j].m_pB[p].m_z < min_z)
						min_z = fibers[i][j].m_pB[p].m_z;
				}

				for (int p = 0; p < fibers[i][j].m_fCount; ++p) {
					if (fibers[i][j].m_pF[p].m_x > max_x)
						max_x = fibers[i][j].m_pF[p].m_x;
					if (fibers[i][j].m_pF[p].m_y > max_y)
						max_y = fibers[i][j].m_pF[p].m_y;
					if (fibers[i][j].m_pF[p].m_z > max_z)
						max_z = fibers[i][j].m_pF[p].m_z;

					if (fibers[i][j].m_pF[p].m_x < min_x)
						min_x = fibers[i][j].m_pF[p].m_x;
					if (fibers[i][j].m_pF[p].m_y < min_y)
						min_y = fibers[i][j].m_pF[p].m_y;
					if (fibers[i][j].m_pF[p].m_z < min_z)
						min_z = fibers[i][j].m_pF[p].m_z;
				}			
			}
		}
	}

	float dx = max_x - min_x;
	float dy = max_y - min_y;
	float dz = max_z - min_z;
	float mx = (max_x + min_x) / 2.0f;
	float my = (max_y + min_y) / 2.0f;
	float mz = (max_z + min_z) / 2.0f;

	float scale = 0.6f / dy;

	/* create vertex buffer and color buffer */
	firstID = new int[fiberCount];
	vertexCount = new int[fiberCount];
	vertices = new float[totalVericesCount*3];
	colors = new float[totalVericesCount*4];
	
	int currentCount = 0;
	int fiberID = 0;
	for (int i = 0; i < w*h; ++i) {
		for (int j = 0; j < count[i]; ++j) {
			if (fibers[i][j].m_fCount > 1 && fibers[i][j].m_bCount > 1) {
				firstID[fiberID] = currentCount;
				vertexCount[fiberID] = fibers[i][j].m_fCount + fibers[i][j].m_bCount - 1;

				for (int p = fibers[i][j].m_bCount-1; p > 0; --p) {
					vertices[currentCount*3+0] = (fibers[i][j].m_pB[p].m_x - mx) * scale;
					vertices[currentCount*3+1] = (fibers[i][j].m_pB[p].m_y - my) * scale;
					vertices[currentCount*3+2] = (fibers[i][j].m_pB[p].m_z - mz) * scale;

					colors[currentCount*4+0] = fibers[i][j].m_pBColor[p].m_x;
					colors[currentCount*4+1] = fibers[i][j].m_pBColor[p].m_y;
					colors[currentCount*4+2] = fibers[i][j].m_pBColor[p].m_z;
					colors[currentCount*4+3] = 0.5f;		// alpha
					
					currentCount++;
				}

				for (int p = 0; p < fibers[i][j].m_fCount; ++p) {
					vertices[currentCount*3+0] = (fibers[i][j].m_pF[p].m_x - mx) * scale;
					vertices[currentCount*3+1] = (fibers[i][j].m_pF[p].m_y - my) * scale;
					vertices[currentCount*3+2] = (fibers[i][j].m_pF[p].m_z - mz) * scale;

					colors[currentCount*4+0] = fibers[i][j].m_pFColor[p].m_x;
					colors[currentCount*4+1] = fibers[i][j].m_pFColor[p].m_y;
					colors[currentCount*4+2] = fibers[i][j].m_pFColor[p].m_z;
					colors[currentCount*4+3] = 0.5f;		// alpha
					
					currentCount++;
				}
				
				fiberID++;
			}
		}
	}
}

void NextStep(int p)
{
	fiber_length++;
}

int main(int argc, char *argv[])
{
	/****************************************/
	/*   Initialize GLUT and create window  */
	/****************************************/
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
	glutInitWindowPosition(50, 50);
	glutInitWindowSize(1024, 1024);

	main_window = glutCreateWindow ("Fibers");
	glutDisplayFunc(myGlutDisplay);
	glutReshapeFunc(myGlutReshape);


	/****************************************/
	/*         Here's the GLUI code         */
	/****************************************/

	GLUI *glui = GLUI_Master.create_glui( "GLUI" );
	
	glui->set_main_gfx_window( main_window );
	GLUI_Panel *panel = glui->add_panel("Fibers");
	glui->add_rotation_to_panel(panel, "ArcBall", rotation);
	glui->add_column_to_panel(panel, false);
	glui->add_translation_to_panel(panel, "Scale", GLUI_TRANSLATION_Z, &translate_z);
	glui->add_column_to_panel(panel, false);
	glui->add_translation_to_panel(panel, "Translate", GLUI_TRANSLATION_XY, translate_xy);

	glui->add_button_to_panel(panel, "next step", -1, NextStep);
	 
	/* We register the idle callback with GLUI, *not* with GLUT */
	GLUI_Master.set_glutIdleFunc(myGlutIdle);

	CreateILRender();
	CreateVertexAndColorBuffer(argv[1], argv[2]);

	glutMainLoop();

	return 0;
}